﻿using System;
using System.Collections.Generic;
using System.Text;
using Evaluant.OPath.Expressions;

namespace Evaluant.OPath
{
    /// <summary>
    /// Finds constraints with only one identifier inside
    /// </summary>

    public class OPathOptimizer : OPathVisitor
    {
        public override void Visit(Evaluant.OPath.Expressions.Path path)
        {
            foreach (Identifier i in path.Identifiers)
            {
                i.Accept(this);
            }
        }

        public override void Visit(Evaluant.OPath.Expressions.Identifier identifier)
        {
            // Example: Person[IsAuthorized]

            if (identifier.Constraint != null)
            {
                if (identifier.Constraint is Path)
                {
                    if (((Path)identifier.Constraint).Identifiers.Count != 1
                        || ((Path)identifier.Constraint).Identifiers[0].Constraint != null)
                    {
                        throw new OPathException("A constraint must be an expression or a boolean value");
                    }

                    Identifier ic = ((Path)identifier.Constraint).Identifiers[0];

                    Path variableName = new Path();
                    variableName.Identifiers.Add(ic);

                    identifier.Constraint = new BinaryOperator(BinaryOperatorEnum.Equal, variableName, new Value("true", ValueEnum.Boolean));
                }
                else
                {
                    identifier.Constraint.Accept(this);
                }
            }
        }

        public override void Visit(Evaluant.OPath.Expressions.Function function)
        {
            if (function.Path != null)
            {
                function.Path.Accept(this);
            }
        }

        public override void Visit(Evaluant.OPath.Expressions.BinaryOperator binaryop)
        {
            if (binaryop.LeftOperand != null)
            {
                if (binaryop.LeftOperand is Path && (binaryop.Type == BinaryOperatorEnum.And || binaryop.Type == BinaryOperatorEnum.Or))
                {
                    Path path = (Path)binaryop.LeftOperand;

                    if (path.Identifiers.Count == 1 && path.Identifiers[0].Constraint == null)
                    {
                        Path variableName = new Path();
                        variableName.Identifiers.Add(path.Identifiers[0]);

                        binaryop.LeftOperand = new BinaryOperator(BinaryOperatorEnum.Equal, variableName, new Value("true", ValueEnum.Boolean));
                    }
                }
                else
                {
                    binaryop.LeftOperand.Accept(this);
                }
            }

            if (binaryop.RightOperand != null)
            {
                if (binaryop.RightOperand is Path && (binaryop.Type == BinaryOperatorEnum.And || binaryop.Type == BinaryOperatorEnum.Or))
                {
                    Path path = (Path)binaryop.RightOperand;

                    if (path.Identifiers.Count == 1 && path.Identifiers[0].Constraint == null)
                    {
                        Path variableName = new Path();
                        variableName.Identifiers.Add(path.Identifiers[0]);

                        binaryop.RightOperand = new BinaryOperator(BinaryOperatorEnum.Equal, variableName, new Value("true", ValueEnum.Boolean));
                    }
                }
                else
                {
                    binaryop.RightOperand.Accept(this);
                }
            }

        }

        public override void Visit(Evaluant.OPath.Expressions.UnaryOperator unaryop)
        {
            // Example: Person[not(IsAuthorized)]

            if (unaryop.Operand != null)
            {
                if (unaryop.Operand is Path)
                {
                    Path path = (Path)unaryop.Operand;

                    if (path.Identifiers.Count == 1 && path.Identifiers[0].Constraint == null)
                    {
                        Path variableName = new Path();
                        variableName.Identifiers.Add(path.Identifiers[0]);

                        unaryop.Operand = new BinaryOperator(BinaryOperatorEnum.Equal, variableName, new Value("true", ValueEnum.Boolean));
                    }
                }
                else
                {
                    unaryop.Operand.Accept(this);
                }
            }
        }

        public override void Visit(Evaluant.OPath.Expressions.Call call)
        {
            foreach (Constraint c in call.Operands)
            {
                if (c != null)
                {
                    c.Accept(this);
                }
            }
        }

        public override void Visit(Evaluant.OPath.Expressions.Value val)
        {
        }
    }
}
